package io.xdag.io;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;

import javolution.JavolutionError;
import javolution.io.UTF8ByteBufferReader;
import javolution.io.UTF8ByteBufferWriter;
import javolution.lang.Configurable;
import javolution.lang.MathLib;
import javolution.lang.Reflection;

public class Struct {
	public static final Configurable<Integer> MAXIMUM_ALIGNMENT = new Configurable<Integer>(new Integer(4));
	public static final BigInteger MAX_VALUE_LONG   = new BigInteger("9223372036854775808");
	public static final BigInteger MAX_VALUE        = new BigInteger("18446744073709551615");
	protected Struct _outer;
	private ByteBuffer _byteBuffer;
	protected int _outerOffset;
	private int _bitsUsed;
	protected int _alignment = 1;
	private int _bitIndex;
	private boolean _resetIndex;
	private byte[] _bytes;
	private static final Reflection.Method ADDRESS_METHOD = Reflection.getMethod("sun.nio.ch.DirectBuffer.address()");

	private static final char[] HEXA = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
			'F' };

	private static final Class<? extends Bool[]> BOOL = new Bool[0].getClass();

	private static final Class<? extends Signed8[]> SIGNED_8 = new Signed8[0].getClass();

	private static final Class<? extends Unsigned8[]> UNSIGNED_8 = new Unsigned8[0].getClass();

	private static final Class<? extends Signed16[]> SIGNED_16 = new Signed16[0].getClass();

	private static final Class<? extends Unsigned16[]> UNSIGNED_16 = new Unsigned16[0].getClass();

	private static final Class<? extends Signed32[]> SIGNED_32 = new Signed32[0].getClass();

	private static final Class<? extends Unsigned32[]> UNSIGNED_32 = new Unsigned32[0].getClass();

	private static final Class<? extends Unsigned64[]> UNSIGNED_64 = new Unsigned64[0].getClass();

	private static final Class<? extends Signed64[]> SIGNED_64 = new Signed64[0].getClass();

	private static final Class<? extends Float32[]> FLOAT_32 = new Float32[0].getClass();

	private static final Class<? extends Float64[]> FLOAT_64 = new Float64[0].getClass();

	public Struct() {
		this._resetIndex = isUnion();
	}

	public final int size() {
		int nbrOfBytes = this._bitsUsed + 7 >> 3;
		return nbrOfBytes % this._alignment == 0 ? nbrOfBytes
				: nbrOfBytes + this._alignment - nbrOfBytes % this._alignment;
	}

	public final ByteBuffer getByteBuffer() {
		if (this._outer != null)
			return this._outer.getByteBuffer();
		return this._byteBuffer != null ? this._byteBuffer : newBuffer();
	}

	private synchronized ByteBuffer newBuffer() {
		if (this._byteBuffer != null)
			return this._byteBuffer;
		int size = size();

		int capacity = isPacked() ? size + 8 - (size & 0x7) : (size & 0x7) == 0 ? size : size;

		ByteBuffer bf = ByteBuffer.allocateDirect(capacity);
		bf.order(byteOrder());
		setByteBuffer(bf, 0);
		return this._byteBuffer;
	}

	public final Struct setByteBuffer(ByteBuffer byteBuffer, int position) {
		if (byteBuffer.order() != byteOrder()) {
			throw new IllegalArgumentException(
					"The byte order of the specified byte buffer is different from this struct byte order");
		}

		if (this._outer != null) {
			throw new UnsupportedOperationException("Inner struct byte buffer is inherited from outer");
		}
		this._byteBuffer = byteBuffer;
		this._outerOffset = position;
		return this;
	}

	public final Struct setByteBufferPosition(int position) {
		return setByteBuffer(getByteBuffer(), position);
	}

	public final int getByteBufferPosition() {
		return this._outer != null ? this._outer.getByteBufferPosition() + this._outerOffset : this._outerOffset;
	}

	public int read(InputStream in) throws IOException {
		ByteBuffer buffer = getByteBuffer();
		if (buffer.hasArray()) {
			int offset = buffer.arrayOffset() + getByteBufferPosition();
			return in.read(buffer.array(), offset, size());
		}
		synchronized (buffer) {
			if (this._bytes == null) {
				this._bytes = new byte[size()];
			}
			int bytesRead = in.read(this._bytes);
			buffer.position(getByteBufferPosition());
			buffer.put(this._bytes);
			return bytesRead;
		}
	}

	public void write(OutputStream out) throws IOException {
		ByteBuffer buffer = getByteBuffer();
		if (buffer.hasArray()) {
			int offset = buffer.arrayOffset() + getByteBufferPosition();
			out.write(buffer.array(), offset, size());
		} else {
			synchronized (buffer) {
				if (this._bytes == null) {
					this._bytes = new byte[size()];
				}
				buffer.position(getByteBufferPosition());
				buffer.get(this._bytes);
				out.write(this._bytes);
			}
		}
	}

	public final long address() {
		ByteBuffer thisBuffer = getByteBuffer();
		if (ADDRESS_METHOD != null) {
			Long start = (Long) ADDRESS_METHOD.invoke(thisBuffer);
			return start.longValue() + getByteBufferPosition();
		}
		throw new UnsupportedOperationException("Operation not supported for " + thisBuffer.getClass());
	}

	public String toString() {
		int size = size();
		StringBuffer sb = new StringBuffer(size * 3);
		ByteBuffer buffer = getByteBuffer();
		int start = getByteBufferPosition();
		for (int i = 0; i < size; i++) {
			int b = buffer.get(start + i) & 0xFF;
			sb.append(HEXA[(b >> 4)]);
			sb.append(HEXA[(b & 0xF)]);
			sb.append((i & 0xF) == 15 ? '\n' : ' ');
		}
		return sb.toString();
	}

	public boolean isUnion() {
		return false;
	}

	public ByteOrder byteOrder() {
		return this._outer != null ? this._outer.byteOrder() : ByteOrder.BIG_ENDIAN;
	}

	public boolean isPacked() {
		return this._outer != null ? this._outer.isPacked() : false;
	}

	protected <S extends Struct> S inner(S struct) {
		if (struct._outer != null) {
			throw new IllegalArgumentException("struct: Already an inner struct");
		}
		struct._outer = this;
		int bitSize = struct.size() << 3;
		updateIndexes(struct._alignment, bitSize, bitSize);
		struct._outerOffset = (this._bitIndex - bitSize >> 3);
		return struct;
	}

	@SuppressWarnings("unchecked")
	protected <S extends Struct> S[] array(S[] structs) {
		Class<?> structClass = null;
		boolean resetIndexSaved = this._resetIndex;
		if (this._resetIndex) {
			this._bitIndex = 0;
			this._resetIndex = false;
		}
		for (int i = 0; i < structs.length;) {
			Struct struct = structs[i];
			if (struct == null) {
				try {
					if (structClass == null) {
						String arrayName = structs.getClass().getName();
						String structName = arrayName.substring(2, arrayName.length() - 1);

						structClass = Reflection.getClass(structName);
						if (structClass == null) {
							throw new JavolutionError("Struct class: " + structName + " not found");
						}
					}
					struct = (Struct) structClass.newInstance();
				} catch (Exception e) {
					throw new JavolutionError(e);
				}
			}
			structs[(i++)] = (S) inner(struct);
		}
		this._resetIndex = resetIndexSaved;
		return (S[]) structs;
	}

	protected <S extends Struct> S[][] array(S[][] structs) {
		boolean resetIndexSaved = this._resetIndex;
		if (this._resetIndex) {
			this._bitIndex = 0;
			this._resetIndex = false;
		}
		for (int i = 0; i < structs.length; i++) {
			array(structs[i]);
		}
		this._resetIndex = resetIndexSaved;
		return (S[][]) structs;
	}

	protected <S extends Struct> S[][][] array(S[][][] structs) {
		boolean resetIndexSaved = this._resetIndex;
		if (this._resetIndex) {
			this._bitIndex = 0;
			this._resetIndex = false;
		}
		for (int i = 0; i < structs.length; i++) {
			array(structs[i]);
		}
		this._resetIndex = resetIndexSaved;
		return (S[][][]) structs;
	}

	@SuppressWarnings("unchecked")
	protected <M extends Member<?>, T> M[] array(T[] t,M[] arrayMember) {
		if(t.length!=arrayMember.length) {
			return (M[]) arrayMember;
		}
		boolean resetIndexSaved = this._resetIndex;
		if (this._resetIndex) {
			this._bitIndex = 0;
			this._resetIndex = false;
		}
		int i;
		if (BOOL.isInstance(arrayMember)) {
			for (i = 0; i < arrayMember.length;)
				arrayMember[(i++)] = (M) (new Bool());
		} else {
			int i1;
			if (SIGNED_8.isInstance(arrayMember)) {
				for (i1 = 0; i1 < arrayMember.length;)
					arrayMember[(i1++)] = (M) new Signed8();
			} else {
				int i2;
				if (UNSIGNED_8.isInstance(arrayMember)) {
					for (i2 = 0; i2 < arrayMember.length;)
						arrayMember[(i2++)] = (M) new Unsigned8();
				} else {
					int i3;
					if (SIGNED_16.isInstance(arrayMember)) {
						for (i3 = 0; i3 < arrayMember.length;)
							arrayMember[(i3++)] = (M) new Signed16();
					} else {
						int i4;
						if (UNSIGNED_16.isInstance(arrayMember)) {
							for (i4 = 0; i4 < arrayMember.length;)
								arrayMember[(i4++)] = (M) new Unsigned16();
						} else {
							int i5;
							if (SIGNED_32.isInstance(arrayMember)) {
								for (i5 = 0; i5 < arrayMember.length;)
									arrayMember[(i5++)] = (M) new Signed32();
							} else {
								int i6;
								if (UNSIGNED_32.isInstance(arrayMember)) {
									for (i6 = 0; i6 < arrayMember.length;)
										arrayMember[(i6++)] = (M) new Unsigned32();
								} else {
									int i7;
									if (SIGNED_64.isInstance(arrayMember)) {
										for (i7 = 0; i7 < arrayMember.length;)
											arrayMember[(i7++)] = (M) new Signed64();
									} else {
										int i8;
										if (FLOAT_32.isInstance(arrayMember)) {
											for (i8 = 0; i8 < arrayMember.length;)
												arrayMember[(i8++)] = (M) new Float32();
										} else {
											int i9;
											if (FLOAT_64.isInstance(arrayMember))
												for (i9 = 0; i9 < arrayMember.length;)
													arrayMember[(i9++)] = (M) new Float64();
											else {
												int i10;
												if (UNSIGNED_64.isInstance(arrayMember)) {
													for (i10 = 0; i10 < arrayMember.length;)
														arrayMember[(i10++)] = (M) new Unsigned64();
												} else {
													throw new UnsupportedOperationException(
															"Cannot create member elements, the arrayMember should contain the member instances instead of null");
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		this._resetIndex = resetIndexSaved;
		return (M[]) arrayMember;
	}
	
	
	@SuppressWarnings("unchecked")
	protected <M extends Member<?>> M[] array(M[] arrayMember) {
		boolean resetIndexSaved = this._resetIndex;
		if (this._resetIndex) {
			this._bitIndex = 0;
			this._resetIndex = false;
		}
		int i;
		if (BOOL.isInstance(arrayMember)) {
			for (i = 0; i < arrayMember.length;)
				arrayMember[(i++)] = (M) new Bool();
		} else {
			int i1;
			if (SIGNED_8.isInstance(arrayMember)) {
				for (i1 = 0; i1 < arrayMember.length;)
					arrayMember[(i1++)] = (M) new Signed8();
			} else {
				int i2;
				if (UNSIGNED_8.isInstance(arrayMember)) {
					for (i2 = 0; i2 < arrayMember.length;)
						arrayMember[(i2++)] = (M) new Unsigned8();
				} else {
					int i3;
					if (SIGNED_16.isInstance(arrayMember)) {
						for (i3 = 0; i3 < arrayMember.length;)
							arrayMember[(i3++)] = (M) new Signed16();
					} else {
						int i4;
						if (UNSIGNED_16.isInstance(arrayMember)) {
							for (i4 = 0; i4 < arrayMember.length;)
								arrayMember[(i4++)] = (M) new Unsigned16();
						} else {
							int i5;
							if (SIGNED_32.isInstance(arrayMember)) {
								for (i5 = 0; i5 < arrayMember.length;)
									arrayMember[(i5++)] = (M) new Signed32();
							} else {
								int i6;
								if (UNSIGNED_32.isInstance(arrayMember)) {
									for (i6 = 0; i6 < arrayMember.length;)
										arrayMember[(i6++)] = (M) new Unsigned32();
								} else {
									int i7;
									if (SIGNED_64.isInstance(arrayMember)) {
										for (i7 = 0; i7 < arrayMember.length;)
											arrayMember[(i7++)] = (M) new Signed64();
									} else {
										int i8;
										if (FLOAT_32.isInstance(arrayMember)) {
											for (i8 = 0; i8 < arrayMember.length;)
												arrayMember[(i8++)] = (M) new Float32();
										} else {
											int i9;
											if (FLOAT_64.isInstance(arrayMember))
												for (i9 = 0; i9 < arrayMember.length;)
													arrayMember[(i9++)] = (M) new Float64();
											else {
												int i10;
												if (UNSIGNED_64.isInstance(arrayMember)) {
													for (i10 = 0; i10 < arrayMember.length;)
														arrayMember[(i10++)] = (M) new Unsigned64();
												} else {
													throw new UnsupportedOperationException(
															"Cannot create member elements, the arrayMember should contain the member instances instead of null");
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		this._resetIndex = resetIndexSaved;
		return (M[]) arrayMember;
	}

	protected <M extends Member<?>> M[][] array(M[][] arrayMember) {
		boolean resetIndexSaved = this._resetIndex;
		if (this._resetIndex) {
			this._bitIndex = 0;
			this._resetIndex = false;
		}
		for (int i = 0; i < arrayMember.length; i++) {
			array(arrayMember[i]);
		}
		this._resetIndex = resetIndexSaved;
		return (M[][]) arrayMember;
	}

	protected <M extends Member<?>> M[][][] array(M[][][] arrayMember) {
		boolean resetIndexSaved = this._resetIndex;
		if (this._resetIndex) {
			this._bitIndex = 0;
			this._resetIndex = false;
		}
		for (int i = 0; i < arrayMember.length; i++) {
			array(arrayMember[i]);
		}
		this._resetIndex = resetIndexSaved;
		return (M[][][]) arrayMember;
	}

	protected UTF8String[] array(UTF8String[] array, int stringLength) {
		for (int i = 0; i < array.length; i++) {
			array[i] = new UTF8String(stringLength);
		}
		return array;
	}

	private int updateIndexes(int alignment, int nbrOfBits, int capacity) {
		if (nbrOfBits > capacity) {
			throw new IllegalArgumentException("nbrOfBits: " + nbrOfBits + " exceeds capacity: " + capacity);
		}

		if (this._resetIndex) {
			this._bitIndex = 0;
		}

		boolean isPacked = isPacked();
		alignment = isPacked ? 1 : MathLib.min(alignment, ((Integer) MAXIMUM_ALIGNMENT.get()).intValue());

		if (this._alignment < alignment) {
			this._alignment = alignment;
		}

		int i = this._bitIndex % (alignment << 3);
		int paddingBits = i == 0 ? 0 : (alignment << 3) - i;

		boolean isBitField = nbrOfBits != capacity;
		if ((isPacked) || ((isBitField) && (nbrOfBits != 0) && (paddingBits <= nbrOfBits))) {
			paddingBits = 0;
		}

		int offset = (this._bitIndex + paddingBits) / (alignment << 3) * alignment;

		this._bitIndex += paddingBits + nbrOfBits;

		if (this._bitsUsed < this._bitIndex) {
			this._bitsUsed = this._bitIndex;
		}

		return offset;
	}

	public class Enum64 extends Struct.Member<Enum<?>> {
		private final long _mask;
		private final int _shift;
		private final int _signShift;
		private final List<?> _enumValues;

		public Enum64(List<?> enumValues) {
			this(enumValues, 64);
		}

		public Enum64(List<?> enumValues, int nbrOfBits) {
			super(8, nbrOfBits, 64);
			this._enumValues = enumValues;
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 64 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = (nbrOfBits == 64 ? -1L : (1L << nbrOfBits) - 1L << this._shift);

			this._signShift = (64 - this._shift - nbrOfBits);
		}

		public Enum<?> get() {
			if (this._mask == -1L) {
				return (Enum<?>) this._enumValues.get((int) Struct.this.getByteBuffer().getLong(position()));
			}

			long value = Struct.this.getByteBuffer().getLong(position());
			value &= this._mask;
			value <<= this._signShift;
			value >>= this._signShift + this._shift;
			return (Enum<?>) this._enumValues.get((int) value);
		}

		public void set(Enum<?> e) {
			int index = e.ordinal();
			if (this._enumValues.get(index) != e) {
				throw new IllegalArgumentException(
						"enum: " + e + ", ordinal value does not reflect enum values position");
			}

			long value = index;
			if (this._mask == -1L) {
				Struct.this.getByteBuffer().putLong(position(), value);
			} else {
				value <<= this._shift;
				value &= this._mask;
				long orMask = Struct.this.getByteBuffer().getLong(position()) & (this._mask ^ 0xFFFFFFFF);
				Struct.this.getByteBuffer().putLong(position(), orMask | value);
			}
		}

		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Enum<?> getValue() {
			return this.get();
		}

		@Override
		public void setValue(Enum<?> val) {
			this.set(val);
		}
	}

	public class Enum32 extends Struct.Member<Enum<?>> {
		private final int _mask;
		private final int _shift;
		private final int _signShift;
		private final List<?> _enumValues;

		public Enum32(List<?> enumValues) {
			this(enumValues, 32);
		}

		public Enum32(List<?> enumValues, int nbrOfBits) {
			super(4, nbrOfBits, 32);
			this._enumValues = enumValues;
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 32 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = (nbrOfBits == 32 ? -1 : (1 << nbrOfBits) - 1 << this._shift);

			this._signShift = (32 - this._shift - nbrOfBits);
		}

		public Enum<?> get() {
			if (this._mask == -1) {
				return (Enum<?>) this._enumValues.get(Struct.this.getByteBuffer().getInt(position()));
			}

			int value = Struct.this.getByteBuffer().getInt(position());
			value &= this._mask;
			value <<= this._signShift;
			value >>= this._signShift + this._shift;
			return (Enum<?>) this._enumValues.get(value);
		}

		public void set(Enum<?> e) {
			int index = e.ordinal();
			if (this._enumValues.get(index) != e) {
				throw new IllegalArgumentException(
						"enum: " + e + ", ordinal value does not reflect enum values position");
			}

			int value = index;
			if (this._mask == -1) {
				Struct.this.getByteBuffer().putInt(position(), value);
			} else {
				value <<= this._shift;
				value &= this._mask;
				int orMask = Struct.this.getByteBuffer().getInt(position()) & (this._mask ^ 0xFFFFFFFF);
				Struct.this.getByteBuffer().putInt(position(), orMask | value);
			}
		}
		
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Enum<?> getValue() {
			return this.get();
		}

		@Override
		public void setValue(Enum<?> val) {
			set(val);
		}
	}

	public class Enum16 extends Struct.Member<Enum<?>> {
		private final int _mask;
		private final int _shift;
		private final int _signShift;
		private final List<?> _enumValues;

		public Enum16(List<?> enumValues) {
			this(enumValues, 16);
		}

		public Enum16(List<?> enumValues, int nbrOfBits) {
			super(2, nbrOfBits, 16);
			this._enumValues = enumValues;
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 16 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = ((1 << nbrOfBits) - 1 << this._shift);
			this._signShift = (32 - this._shift - nbrOfBits);
		}

		public Enum<?> get() {
			if (this._mask == 65535) {
				return (Enum<?>) this._enumValues.get(Struct.this.getByteBuffer().getShort(position()));
			}

			int value = Struct.this.getByteBuffer().getShort(position());
			value &= this._mask;
			value <<= this._signShift;
			value >>= this._signShift + this._shift;
			return (Enum<?>) this._enumValues.get(value);
		}

		public void set(Enum<?> e) {
			int index = e.ordinal();
			if (this._enumValues.get(index) != e) {
				throw new IllegalArgumentException(
						"enum: " + e + ", ordinal value does not reflect enum values position");
			}

			short value = (short) index;
			if (this._mask == 65535) {
				Struct.this.getByteBuffer().putShort(position(), value);
			} else {
				value = (short) (value << this._shift);
				value = (short) (value & this._mask);
				int orMask = Struct.this.getByteBuffer().getShort(position()) & (this._mask ^ 0xFFFFFFFF);
				Struct.this.getByteBuffer().putShort(position(), (short) (orMask | value));
			}
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Enum<?> getValue() {
			return this.get();
		}

		@Override
		public void setValue(Enum<?> val) {
			set(val);
		}
	}

	public class Enum8 extends Struct.Member<Enum<?>> {
		private final int _mask;
		private final int _shift;
		private final int _signShift;
		private final List<?> _enumValues;

		public Enum8(List<?> enumValues) {
			this(enumValues, 8);
		}

		public Enum8(List<?> enumValues, int nbrOfBits) {
			super(1, nbrOfBits, 8);
			this._enumValues = enumValues;
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 8 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = ((1 << nbrOfBits) - 1 << this._shift);
			this._signShift = (32 - this._shift - nbrOfBits);
		}

		public Enum<?> get() {
			if (this._mask == 255) {
				return (Enum<?>) this._enumValues.get(Struct.this.getByteBuffer().get(position()));
			}
			int value = Struct.this.getByteBuffer().get(position());
			value &= this._mask;
			value <<= this._signShift;
			value >>= this._signShift + this._shift;
			return (Enum<?>) this._enumValues.get(value);
		}

		public void set(Enum<?> e) {
			int index = e.ordinal();
			if (this._enumValues.get(index) != e) {
				throw new IllegalArgumentException(
						"enum: " + e + ", ordinal value does not reflect enum values position");
			}

			byte value = (byte) index;
			if (this._mask == 255) {
				Struct.this.getByteBuffer().put(position(), value);
			} else {
				value = (byte) (value << this._shift);
				value = (byte) (value & this._mask);
				int orMask = Struct.this.getByteBuffer().get(position()) & (this._mask ^ 0xFFFFFFFF);
				Struct.this.getByteBuffer().put(position(), (byte) (orMask | value));
			}
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Enum<?> getValue() {
			return this.get();
		}

		@Override
		public void setValue(Enum<?> val) {
			set(val);
		}
	}

	public class Reference64<S extends Struct> extends Struct.Member<S> {
		private S _struct;

		public Reference64() {
			super(8, 8);
		}

		public void set(S struct) {
			if (struct != null)
				Struct.this.getByteBuffer().putLong(position(), struct.address());
			else if (struct == null) {
				Struct.this.getByteBuffer().putLong(position(), 0L);
			}
			this._struct = struct;
		}

		public S get() {
			return this._struct;
		}

		public long value() {
			return Struct.this.getByteBuffer().getLong(position());
		}

		public boolean isUpToDate() {
			if (this._struct != null) {
				return Struct.this.getByteBuffer().getLong(position()) == this._struct.address();
			}
			return Struct.this.getByteBuffer().getLong(position()) == 0L;
		}

		@Override
		public S getValue() {
			return this.get();
		}

		@Override
		public void setValue(S val) {
			set(val);
		}

		@Override
		public String toString() {
			// TODO Auto-generated method stub
			return null;
		}
	}

	public class Reference32<S extends Struct> extends Struct.Member<S> {
		private S _struct;

		public Reference32() {
			super(4, 4);
		}

		public void set(S struct) {
			if (struct != null)
				Struct.this.getByteBuffer().putInt(position(), (int) struct.address());
			else {
				Struct.this.getByteBuffer().putInt(position(), 0);
			}
			this._struct = struct;
		}

		public S get() {
			return this._struct;
		}

		public int value() {
			return Struct.this.getByteBuffer().getInt(position());
		}

		public boolean isUpToDate() {
			if (this._struct != null) {
				return Struct.this.getByteBuffer().getInt(position()) == (int) this._struct.address();
			}

			return Struct.this.getByteBuffer().getInt(position()) == 0;
		}

		@Override
		public S getValue() {
			return this.get();
		}

		@Override
		public void setValue(S val) {
			this.set(val);
		}

		@Override
		public String toString() {
			// TODO Auto-generated method stub
			return null;
		}
	}

	public class Float64 extends Struct.Member<Double> {
		public Float64() {
			super(8, 8);
		}

		public void set(double value) {
			Struct.this.getByteBuffer().putDouble(position(), value);
		}

		public double get() {
			return Struct.this.getByteBuffer().getDouble(position());
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Double getValue() {
			return this.get();
		}

		@Override
		public void setValue(Double val) {
			// TODO Auto-generated method stub
			this.set(val.doubleValue());
		}
	}

	public class Float32 extends Struct.Member<Float> {
		public Float32() {
			super(4, 4);
		}

		public void set(float value) {
			Struct.this.getByteBuffer().putFloat(position(), value);
		}

		public float get() {
			return Struct.this.getByteBuffer().getFloat(position());
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Float getValue() {
			return this.get();
		}

		@Override
		public void setValue(Float val) {
			this.set(val.floatValue());
		}
	}

	public class Signed64 extends Struct.Member<Long> {
		private final long _mask;
		private final int _shift;
		private final int _signShift;

		public Signed64() {
			this(64);
		}

		public Signed64(int nbrOfBits) {
			super(8, nbrOfBits, 64);
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 64 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = (nbrOfBits == 64 ? -1L : (1L << nbrOfBits) - 1L << this._shift);

			this._signShift = (64 - this._shift - nbrOfBits);
		}

		public long get() {
			if (this._mask == -1L) {
				return Struct.this.getByteBuffer().getLong(position());
			}
			long value = Struct.this.getByteBuffer().getLong(position());
			value &= this._mask;
			value <<= this._signShift;
			value >>= this._signShift + this._shift;
			return value;
		}

		public void set(long value) {
			if (this._mask == -1L) {
				Struct.this.getByteBuffer().putLong(position(), value);
			} else {
				value <<= this._shift;
				value &= this._mask;
				long orMask = Struct.this.getByteBuffer().getLong(position()) & (this._mask ^ 0xFFFFFFFF);
				Struct.this.getByteBuffer().putLong(position(), orMask | value);
			}
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Long getValue() {
			return this.get();
		}

		@Override
		public void setValue(Long val) {
			this.set(val.longValue());
		}
	}

	public class Unsigned64 extends Struct.Member<BigInteger> {
		private final long _mask;
		private final int _shift;
		private final int _signShift;

		public Unsigned64() {
			this(64);
		}

		public Unsigned64(int nbrOfBits) {
			super(8, nbrOfBits, 64);
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 64 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = (nbrOfBits == 64 ? -1L : (1L << nbrOfBits) - 1L << this._shift);

			this._signShift = (64 - this._shift - nbrOfBits);
		}

		public long get() {
			if (this._mask == -1L) {
				return Struct.this.getByteBuffer().getLong(position());
			}
			long value = Struct.this.getByteBuffer().getLong(position());
			value &= this._mask;
			value <<= this._signShift;
			value >>= this._signShift + this._shift;
			return value;
		}

		public void set(String val) {
			BigInteger bint = new BigInteger(val);
			
			if(bint.compareTo(MAX_VALUE_LONG)<0&&bint.compareTo(BigInteger.ZERO)>=0) {
				long longValue =bint.longValue();
				if (this._mask == -1L) {
					Struct.this.getByteBuffer().putLong(position(), longValue);
				} else {
					longValue <<= this._shift;
					longValue &= this._mask;
					long orMask = Struct.this.getByteBuffer().getLong(position()) & (this._mask ^ 0xFFFFFFFF);
					Struct.this.getByteBuffer().putLong(position(), orMask | longValue);
				}
			}else if(bint.compareTo(MAX_VALUE_LONG)>0&&bint.compareTo(MAX_VALUE)<0){
				int flag=0;
				for(int i=8;i>0;i--) {
					Struct.this.getByteBuffer().put(position()+flag,bint.toByteArray()[i]);
					flag++;
				}
			}else {
				throw new UnsupportedOperationException(
						"Cannot create member elements, number out of range");
			}
			
		}
		@Override
		public String toString() {
			long value = get();
			if(value>=0) {
				return String.valueOf(value);
			}else {
				return BigInteger.valueOf(value & Long.MAX_VALUE).add(MAX_VALUE_LONG).toString();
			}
		}

		@Override
		public BigInteger getValue() {
			return new BigInteger(this.toString()) ;
		}

		@Override
		public void setValue(BigInteger val) {
			this.set(val.toString());
		}
	}

	public class Unsigned32 extends Struct.Member<Long> {
		private final int _shift;
		private final long _mask;

		public Unsigned32() {
			this(32);
		}

		public Unsigned32(int nbrOfBits) {
			super(4, nbrOfBits, 32);
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 32 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = (nbrOfBits == 32 ? 4294967295L : (1L << nbrOfBits) - 1L << this._shift);
		}

		public long get() {
			int value = Struct.this.getByteBuffer().getInt(position());

			int msBitNum = this._bitCount + this._shift;
			if (msBitNum > 32) {
				int adjacentBytes = msBitNum - 32 + 7 >> 3;
				for (int i = 0; i < adjacentBytes; i++) {
					int byteValue = Struct.this.getByteBuffer().get(position() + 2) & 0xFF;
					value |= byteValue << (32 + i << 3);
				}
			}

			return (value & this._mask) >>> this._shift;
		}

		public void set(long value) {
			if (this._mask == -1L) {
				Struct.this.getByteBuffer().putInt(position(), (int) value);
			} else {
				value <<= this._shift;
				value &= this._mask;
				long orMask = Struct.this.getByteBuffer().getInt(position()) & (this._mask ^ 0xFFFFFFFF);

				Struct.this.getByteBuffer().putInt(position(), (int) (orMask | value));

				int msBitNum = this._bitCount + this._shift;
				if (msBitNum > 32) {
					int adjacentBytes = msBitNum - 32 + 7 >> 3;
					for (int i = 0; i < adjacentBytes; i++) {
						int extraShift = 32 + (i << 3);
						byte shiftedByteVal = (byte) (int) (value >>> extraShift);
						byte byteMask = (byte) (int) ((this._mask ^ 0xFFFFFFFF) >>> extraShift);
						byte orByteMask = (byte) (Struct.this.getByteBuffer().get(position() + 4 + i) & byteMask);

						Struct.this.getByteBuffer().put(position() + 4 + i, (byte) (shiftedByteVal | orByteMask));
					}
				}
			}
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Long getValue() {
			return this.get();
		}

		@Override
		public void setValue(Long val) {
			this.set(val.longValue());
		}
	}

	public class Signed32 extends Struct.Member<Integer> {
		private final int _mask;
		private final int _shift;
		private final int _signShift;

		public Signed32() {
			this(32);
		}

		public Signed32(int nbrOfBits) {
			super(4, nbrOfBits, 32);
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 32 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = (nbrOfBits == 32 ? -1 : (1 << nbrOfBits) - 1 << this._shift);

			this._signShift = (32 - this._shift - nbrOfBits);
		}

		public int get() {
			if (this._mask == -1) {
				return Struct.this.getByteBuffer().getInt(position());
			}
			int value = Struct.this.getByteBuffer().getInt(position());
			value &= this._mask;
			value <<= this._signShift;
			value >>= this._signShift + this._shift;
			return value;
		}

		public void set(int value) {
			if (this._mask == -1) {
				Struct.this.getByteBuffer().putInt(position(), value);
			} else {
				value <<= this._shift;
				value &= this._mask;
				int orMask = Struct.this.getByteBuffer().getInt(position()) & (this._mask ^ 0xFFFFFFFF);
				Struct.this.getByteBuffer().putInt(position(), orMask | value);
			}
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Integer getValue() {
			return this.get();
		}

		@Override
		public void setValue(Integer val) {
			this.set(val.intValue());
		}
	}

	public class Unsigned16 extends Struct.Member<Integer> {
		private final int _shift;
		private final int _mask;

		public Unsigned16() {
			this(16);
		}

		public Unsigned16(int nbrOfBits) {
			super(2, nbrOfBits, 16);
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 16 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = ((1 << nbrOfBits) - 1 << this._shift);
		}

		public int get() {
			int value = Struct.this.getByteBuffer().getShort(position());

			int msBitNum = this._bitCount + this._shift;
			if (msBitNum > 16) {
				int adjacentBytes = msBitNum - 16 + 7 >> 3;
				for (int i = 0; i < adjacentBytes; i++) {
					int byteValue = Struct.this.getByteBuffer().get(position() + 2) & 0xFF;
					value |= byteValue << (16 + i << 3);
				}
			}

			return (value & this._mask) >>> this._shift;
		}

		public void set(int value) {
			if (this._mask == 65535) {
				Struct.this.getByteBuffer().putShort(position(), (short) value);
			} else {
				value <<= this._shift;
				value &= this._mask;
				int orMask = Struct.this.getByteBuffer().getShort(position()) & (this._mask ^ 0xFFFFFFFF);

				Struct.this.getByteBuffer().putShort(position(), (short) (orMask | value));

				int msBitNum = this._bitCount + this._shift;
				if (msBitNum > 16) {
					int adjacentBytes = msBitNum - 16 + 7 >> 3;
					for (int i = 0; i < adjacentBytes; i++) {
						int extraShift = 16 + (i << 3);
						byte shiftedByteVal = (byte) (value >>> extraShift);
						byte byteMask = (byte) ((this._mask ^ 0xFFFFFFFF) >>> extraShift);
						byte orByteMask = (byte) (Struct.this.getByteBuffer().get(position() + 2 + i) & byteMask);

						Struct.this.getByteBuffer().put(position() + 2 + i, (byte) (shiftedByteVal | orByteMask));
					}
				}
			}
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Integer getValue() {
			return this.get();
		}

		@Override
		public void setValue(Integer val) {
			this.set(val.intValue());
		}
	}

	public class Signed16 extends Struct.Member<Short> {
		private final int _mask;
		private final int _shift;
		private final int _signShift;

		public Signed16() {
			this(16);
		}

		public Signed16(int nbrOfBits) {
			super(2, nbrOfBits, 16);
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 16 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = ((1 << nbrOfBits) - 1 << this._shift);
			this._signShift = (32 - this._shift - nbrOfBits);
		}

		public short get() {
			if (this._mask == 65535) {
				return Struct.this.getByteBuffer().getShort(position());
			}
			int value = Struct.this.getByteBuffer().getShort(position());
			value &= this._mask;
			value <<= this._signShift;
			value >>= this._signShift + this._shift;
			return (short) value;
		}

		public void set(short value) {
			if (this._mask == 65535) {
				Struct.this.getByteBuffer().putShort(position(), value);
			} else {
				value = (short) (value << this._shift);
				value = (short) (value & this._mask);
				int orMask = Struct.this.getByteBuffer().getShort(position()) & (this._mask ^ 0xFFFFFFFF);
				Struct.this.getByteBuffer().putShort(position(), (short) (orMask | value));
			}
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Short getValue() {
			return this.get();
		}

		@Override
		public void setValue(Short val) {
			this.set(val.shortValue());
		}
	}

	public class Unsigned8 extends Struct.Member<Short> {
		private final int _shift;
		private final int _mask;

		public Unsigned8() {
			this(8);
		}

		public Unsigned8(int nbrOfBits) {
			super(1, nbrOfBits, 8);
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 8 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = ((1 << nbrOfBits) - 1 << this._shift);
		}

		public short get() {
			if (this._bitCount + this._shift > 8) {
				short value = Struct.this.getByteBuffer().getShort(position());
				return (short) ((value & this._mask) >>> this._shift);
			}

			short value = (short) Struct.this.getByteBuffer().get(position());
			return (short) ((value & this._mask) >>> this._shift);
		}

		public void set(short value) {
			if (this._mask == 255) {
				Struct.this.getByteBuffer().put(position(), (byte) value);
			} else {
				value = (short) (value << this._shift);
				value = (short) (value & this._mask);

				if (this._bitCount + this._shift > 8) {
					int orMask = Struct.this.getByteBuffer().getShort(position()) & (this._mask ^ 0xFFFFFFFF);
					Struct.this.getByteBuffer().putShort(position(), (short) (value | orMask));
				} else {
					int orMask = Struct.this.getByteBuffer().get(position()) & (this._mask ^ 0xFFFFFFFF);
					Struct.this.getByteBuffer().put(position(), (byte) (value | orMask));
				}
			}
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Short getValue() {
			return this.get();
		}

		@Override
		public void setValue(Short val) {
			this.set(val.shortValue());
		}
	}

	public class Signed8 extends Struct.Member<Byte> {
		private final int _mask;
		private final int _shift;
		private final int _signShift;

		public Signed8() {
			this(8);
		}

		public Signed8(int nbrOfBits) {
			super(1, nbrOfBits, 8);
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 8 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = ((1 << nbrOfBits) - 1 << this._shift);
			this._signShift = (32 - this._shift - nbrOfBits);
		}

		public byte get() {
			if (this._mask == 255) {
				return Struct.this.getByteBuffer().get(position());
			}
			int value = Struct.this.getByteBuffer().get(position());
			value &= this._mask;
			value <<= this._signShift;
			value >>= this._signShift + this._shift;
			return (byte) value;
		}

		public void set(byte value) {
			if (this._mask == 255) {
				Struct.this.getByteBuffer().put(position(), value);
			} else {
				value = (byte) (value << this._shift);
				value = (byte) (value & this._mask);
				int orMask = Struct.this.getByteBuffer().get(position()) & (this._mask ^ 0xFFFFFFFF);
				Struct.this.getByteBuffer().put(position(), (byte) (orMask | value));
			}
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Byte getValue() {
			return this.get();
		}

		@Override
		public void setValue(Byte val) {
			this.set(val.byteValue());
		}
	}

	public class Bool extends Struct.Member<Boolean> {
		private final int _mask;
		private final int _shift;

		public Bool() {
			this(8);
		}

		public Bool(int nbrOfBits) {
			super(1, nbrOfBits, 8);
			int startBit = offset() << 3;
			this._shift = (Struct.this.byteOrder() == ByteOrder.BIG_ENDIAN ? 8 - Struct.this._bitIndex + startBit
					: Struct.this._bitIndex - startBit - nbrOfBits);

			this._mask = ((1 << nbrOfBits) - 1 << this._shift);
		}

		public boolean get() {
			return (Struct.this.getByteBuffer().get(position()) & this._mask) != 0;
		}

		public void set(boolean value) {
			if (this._mask == 255) {
				Struct.this.getByteBuffer().put(position(), (byte) (value ? 1 : 0));
			} else {
				int prevCleared = Struct.this.getByteBuffer().get(position()) & (this._mask ^ 0xFFFFFFFF);
				if (value) {
					Struct.this.getByteBuffer().put(position(), (byte) (prevCleared | 1 << this._shift));
				} else
					Struct.this.getByteBuffer().put(position(), (byte) prevCleared);
			}
		}
		@Override
		public String toString() {
			return String.valueOf(get());
		}

		@Override
		public Boolean getValue() {
			return Boolean.valueOf(this.get());
		}

		@Override
		public void setValue(Boolean val) {
			set(val.booleanValue());
		}

		
	}

	public class UTF8String extends Struct.Member<String> {
		private final UTF8ByteBufferWriter _writer = new UTF8ByteBufferWriter();

		private final UTF8ByteBufferReader _reader = new UTF8ByteBufferReader();
		private final char[] _chars;
		private final int _length;

		public UTF8String(int length) {
			super(1, length);
			this._length = length;
			this._chars = new char[length];
		}

		public void set(String string) {
			ByteBuffer buffer = Struct.this.getByteBuffer();
			synchronized (buffer) {
				try {
					buffer.position(position());
					this._writer.setOutput(buffer);
					if (string.length() < this._length) {
						this._writer.write(string);
						this._writer.write(0);
					} else if (string.length() > this._length) {
						this._writer.write(string.substring(0, this._length));
					} else {
						this._writer.write(string);
					}
				} catch (IOException e) {
					throw new JavolutionError(e);
				} finally {
					this._writer.reset();
				}
			}
		}

		public String get() {
			ByteBuffer buffer = Struct.this.getByteBuffer();
			synchronized (buffer) {
				try {
					buffer.position(position());
					this._reader.setInput(buffer);
					char c;
					for (int i = 0; i < this._length; this._chars[(i++)] = c) {
						c = (char) this._reader.read();
						if (c == 0) {
							String str = new String(this._chars, 0, i);

							this._reader.reset();
							return str;
						}
					}
					String i = new String(this._chars, 0, this._length);

					this._reader.reset();
					return i;
				} catch (IOException e) {
					throw new JavolutionError(e);
				} finally {
					this._reader.reset();
				}
			}
		}
		@Override
		public String toString() {
			return get();
		}

		@Override
		public String getValue() {
			return this.get();
		}

		@Override
		public void setValue(String val) {
			this.set(val);
		}
	}

	protected abstract class Member<T> {
		private final int _offset;
		protected final int _bitCount;

		protected Member(int alignment, int size) {
			int nbrOfBits = size << 3;
			this._offset = Struct.this.updateIndexes(alignment, nbrOfBits, nbrOfBits);
			this._bitCount = nbrOfBits;
		}

		Member(int alignment, int nbrOfBits, int capacity) {
			this._offset = Struct.this.updateIndexes(alignment, nbrOfBits, capacity);
			this._bitCount = nbrOfBits;
		}

		public abstract T getValue();
		public abstract void setValue(T val);
		public abstract String toString();
		
		public final Struct struct() {
			return Struct.this;
		}

		public final int bitCount() {
			return this._bitCount;
		}

		public final int offset() {
			return this._offset;
		}

		public final int position() {
			return Struct.this.getByteBufferPosition() + this._offset;
		}
	}
}
